#ifndef __RUNNER_HPP__
#define __RUNNER_HPP__
// 运行模块
#include <string>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/resource.h>
#include "../common/util.hpp"
#include "../common/log.hpp"

namespace ns_runner
{
    using namespace ns_util;
    using namespace ns_log;

    class Runner
    {
    public:
        Runner() {}
        ~Runner() {}
    public:
        // 资源限制接口
        // cpu_limit时间限制s  men_limit空间限制KB
        static void SetProCLimit(int cpu_limit, int mem_limit)
        {
            // 时间限制
            struct rlimit cpuLimit;
            cpuLimit.rlim_max = RLIM_INFINITY;
            cpuLimit.rlim_cur = cpu_limit;
            setrlimit(RLIMIT_CPU, &cpuLimit);

            // 空间限制
            struct rlimit memLimit;
            memLimit.rlim_max = RLIM_INFINITY;
            memLimit.rlim_cur = mem_limit * 1024;  // byte->kb
            setrlimit(RLIMIT_AS, &memLimit);
        }
        // 运行模块
        // 输入可执行文件名 在temp目录下的.exe文件
        // 返回值：0表示运行成功、<0表示内部错误，>0表示运行出错
        // 时间限制：cpu_limit  空间限制：men_limit
        // 返回值： <0 内部错误 =0运行成功，成功写入stdout等文件 >0运行中断，用户代码存在问题
        static int Run(const std::string& file_name, int cpu_limit, int mem_limit)
        {
            // 在父进程中先将运行所需要的三个临时文件打开
            int _stdin  = open(PathUtil::Stdin(file_name).c_str(), O_CREAT | O_RDONLY, 0644);
            int _stdout = open(PathUtil::Stdout(file_name).c_str(), O_CREAT | O_WRONLY, 0644);
            int _stderr = open(PathUtil::Stderr(file_name).c_str(), O_CREAT | O_WRONLY, 0644);
            // 打开文件进行差错处理
            if (_stdin < 0 || _stdout < 0 || _stderr < 0)
            {
                // 文件打不开运行程序没有意义了
                LOG(ERROR) << "内部错误, 标准文件打开/创建失败" << "\n";
                return -1;
            }

            pid_t childPid = fork();
            if (childPid < 0)
            {
                // 创建子进程失败
                // 创建失败，打开的文件需要收回-否则占用无效资源！
                close(_stdin);
                close(_stdout);
                close(_stderr);
                LOG(ERROR) << "内部错误, 创建子进程失败" << "\n";
                return -2;
            }
            if (childPid == 0)
            {
                // 子进程
                // 资源限制
                SetProCLimit(cpu_limit, mem_limit);
                dup2(_stdin, 0);
                dup2(_stdout, 1);
                dup2(_stderr, 2);
                execl(PathUtil::Exe(file_name).c_str(), PathUtil::Exe(file_name).c_str(), nullptr);  // execl函数，不从环境变量下找，直接根据路径找可执行文件 路径、可执行文件
                exit(-1);  // 程序替换出错
            }
            // 父进程
            close(_stdin);
            close(_stdout);
            close(_stderr);
            //父进程等待子进程，查看返回情况
            int status;  //输出参数，接收状态信息
            waitpid(childPid, &status, 0);  // 阻塞等待
            if (status < 0)
            {
                //信号不等于0说明执行失败
                LOG(ERROR) << "内部错误, execl参数或者路径不正确" << status << "\n";
                return status;
            }
            else LOG(INFO) << "运行完毕！退出码为: " << (status & 0x7F)<< "\n";
            return (status & 0x7F);
        }
    };
}

#endif